/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ru.niir.goodfil.ui;

import org.apache.log4j.Logger;
import ru.niir.goodfil.audit.domain.ClientUIEvent;
import ru.niir.goodfil.audit.domain.enums.Crud;
import ru.niir.goodfil.db.*;
import ru.niir.goodfil.db.dic.ClientState;
import ru.niir.goodfil.exceptions.CreateFailureException;
import ru.niir.goodfil.exceptions.GoodfilSecurityException;
import ru.niir.goodfil.exceptions.ValidationException;
import ru.niir.goodfil.ui.utils.RestsExportManager;
import ru.niir.goodfil.utils.Pair;
import ru.niir.goodfil.utils.validation.CreateCheckResult;
import ru.niir.goodfil.utils.validation.ValidationError;
import ru.niir.goodfil.utils.validation.ValidationResult;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Бин для страницы Client.jsf
 */
public class ClientBean extends PageBean {
    private static final Logger logger = Logger.getLogger(ClientBean.class);

    /**
     * Проверка правильности email адреса.
     */
    public static final Pattern email = Pattern.compile("^[-a-z0-9!#$%&'*+/=?^_`{|}~]+(?:\\.[-a-z0-9!#$%&'*+/=?^_`{|}~]+)*@(?:[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?\\.)*(?:aero|arpa|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|[a-z][a-z])$");

    /**
     * Проверка правильного url
     */
    public static final Pattern urlSite = Pattern.compile("/^(http:\\/\\/|https:\\/\\/)?([^\\.\\/]+\\.)*([a-zA-Z0-9])([a-zA-Z0-9-]*)\\.([a-zA-Z]{2,4})(\\/.*)?$/i");

    /**
     * Конструктор.
     */
    public ClientBean() {
        //  Разделение прав пользователей -------------------------------------------------------
        //  Администратор может выполнять любые операции с клиентами
        //  Менеджер может создавать клиентов и редактировать своих клиентов

        if (getSecurityBean().getIsRoleStoreman())
            throw new GoodfilSecurityException("Кладовщик не имеет доступа к модулю \"Клиенты\"");

        //  -------------------------------------------------------------------------------------

        //  Загрузка настроек рассылки остатков
        long clientId = getSessionBean().getEditableClientId();
        RestsExport restsExport = gf().getRestExportByClientId(clientId);
        if (restsExport == null) {
            doRestsExport = false;
            restsExportCrontab = RestsExportManager.getFirst();
        } else {
            doRestsExport = true;
            restsExportCrontab = restsExport.getCrontab();
        }
        //  -------------------------------------------------------------------------------------
    }

    public Client getCurrent() {
        Client client = getSessionBean().getEditableClient();
        if (client != null) {
            if (client.getVisibleBrands() == null) {
                client.initVisibleBrands();
            }
            if (client.getVisibleClientGroups() == null) {
                client.initVisibleClientGroups();
            }
        }
        return client;
    }

    /**
     * Добавить или одновить запись клиента в зависимости от того, есть ли клиент с таким id в БД
     */
    public void save() {
        logger.debug("save()");

        if (getCurrent().getManagerId() == null || getCurrent().getManagerId() <= 0)
            facesError("Необходимо указать менеджера");

        if(getCurrent().getClientStateId() == -1)
            facesError("Необходимо выбрать статус клиента");

        for (Long brandId : getSelectedClientgroups().keySet()) {
            Object value = getSelectedClientgroups().get(brandId);
            if (value == null || "0".equals(String.valueOf(value))) {
                facesError("Необходимо выбрать колонки цен");
                break;
            }
        }

        if (hasFacesErrors()) return;

        if (!getSecurityBean().isManagerCorrect(getCurrent().getManagerId())) {
            throw new GoodfilSecurityException("Попытка создать/редактировать клиента чужого менеджера");
        }

        logger.debug("saving...");

        if (getCurrent().getId() == -1) {   //  Новый клиент
            try {
                gf().saveClient(getCurrent());
//                getAuditService().logClientUIEvent(prepareClientUIEvent(Crud.CREATE));

                if (!doRestsExport) gf().removeRestsExport(getCurrent().getId());
                else gf().saveRestsExport(new RestsExport(getCurrent().getId(), restsExportCrontab));

                facesInfo("Клиент был создан");

                getSessionBean().setEditableClient(null);
            } catch (ValidationException ex) {
                logger.error(ex);
                ex.printStackTrace();
                ValidationResult validationResult = ex.getResult();
                for (ValidationError error : validationResult.getErrors()) {
                    facesError(error.toHumanString());
                }
            } catch (CreateFailureException ex) {
                logger.error(ex);
                ex.printStackTrace();
                CreateCheckResult createCheckResult = ex.getResult();
                facesError(createCheckResult.getMsg());
            }


        } else {   //  Старый клиент
            try {
                gf().updateClient(getCurrent());
//                getAuditService().logClientUIEvent(prepareClientUIEvent(Crud.UPDARE));

                if (!doRestsExport) gf().removeRestsExport(getCurrent().getId());
                else gf().saveRestsExport(new RestsExport(getCurrent().getId(), restsExportCrontab));

                facesInfo("Данные о клиенте были обновлены");

                getSessionBean().setEditableClient(null);
            } catch (ValidationException ex) {
                logger.error(ex);
                ex.printStackTrace();
                ValidationResult validationResult = ex.getResult();
                for (ValidationError error : validationResult.getErrors()) {
                    facesError(error.toHumanString());
                }
            }
        }
    }

    private ClientUIEvent prepareClientUIEvent(Crud crud) {
        Client client = getCurrent();

        String managerFio = null;
        User manager = gf().getUserById(client.getManagerId());
        if (manager != null) {
            managerFio = manager.getFio();
        }

        ClientUIEvent event = new ClientUIEvent();
        event.setIp(getAuditHelper().getIp());
        event.setLogin(getAuditHelper().getLogin());
        event.setCrud(crud);
        event.setClientId(String.valueOf(client.getId()));
        event.setName(client.getCompanyName());
        event.setC1Num(client.getC1ClientNum());
        event.setManagerName(managerFio);
        event.setDebtBank(String.valueOf(client.getDebtBank()));
        event.setDebtCash(String.valueOf(client.getDebtCash()));

        return event;
    }

    /**
     * -------------------------------------------------------------------------
     * ----------Валидаторы----------
     * -------------------------------------------------------------------------
     */

    /**
     * @param word
     * @param patternString
     * @return
     */
    public static boolean matches(String word, String patternString) {
        return Pattern.compile(patternString).matcher(word).matches();
    }

    /**
     * Получить название поля по его идентификатору.
     * Эту безусловно тупую функция пришлось написать из-за того, что мне не удалось заставить работать validationMessage
     *
     * @param fieldId идентификатор поля h:inputText
     * @return его название на русском
     */
    private String getFieldNameByFieldId(String fieldId) {
        if (fieldId.equals("tbAccount")) return "Р/счет";
        if (fieldId.equals("tbAccountCor")) return "К/счет";
        if (fieldId.equals("tbBik")) return "БИК";
        if (fieldId.equals("tbInn")) return "ИНН";
        if (fieldId.equals("tbKpp")) return "КПП";
        if (fieldId.equals("tbOkonh")) return "ОКОНХ";
        if (fieldId.equals("tbOkpo")) return "ОКПО";
        if (fieldId.equals("tbOgrn")) return "ОГРН";
        return null;
    }

    /**
     * Функция проверяет, что поле содержит только цифры с помощью простого регулярного выражения: \\d*.
     * При этом генерируется сообщение об ошибке вида "Поле <такое-то> должно содержать только циры".
     * Имя поля вычисляется функцией getFieldNameByFieldId
     */
    public void validateDigits(FacesContext context, UIComponent component, Object obj) {
        if (!matches(obj.toString(), "\\d*")) {
            facesError(context, component, "Поле " + getFieldNameByFieldId(component.getId()) + " должно содержать только цифры");
        }
    }

    /**
     * Проверка поля долга по банку.
     *
     * @param context
     * @param component
     * @param obj
     */
    public void validateDebtBank(FacesContext context, UIComponent component, Object obj) {
        try {
            float value = (Float) obj;
            if (value >= 0) return;
        } catch (Exception e) {
            e.printStackTrace();
        }

        facesError(context, component, "Долг по банку должен быть положительным числом.");
    }

    /**
     * Проверка поля долга по кассе
     *
     * @param context
     * @param component
     * @param obj
     */
    public void validateDebtCache(FacesContext context, UIComponent component, Object obj) {
        try {
            float value = (Float) obj;
            if (value >= 0) return;
        } catch (Exception e) {
            e.printStackTrace();
        }

        facesError(context, component, "Долг по кассе должен быть положительным числом.");
    }

    /**
     * Проверка поля e-mail.
     *
     * @param context
     * @param component
     * @param obj
     */
    public void validateEmail(FacesContext context, UIComponent component, Object obj) {
        String clientE_Mail = obj.toString();
        if (!clientE_Mail.equals("")) {
            Matcher matcher = email.matcher(clientE_Mail);
            if (!matcher.matches()) {
                String field = "";
                if (component.getId().equals("tbBossEmail")) field = "Email руководителя";
                if (component.getId().equals("tbContactEmail")) field = "Email менеджера";
                if (component.getId().equals("tbBuhEmail")) field = "Email бухгалтера";
                facesError("Поле " + field + " заполнено не верно.");
            }
        }
    }

    private boolean doRestsExport = false;

    public boolean isDoRestsExport() {
        return doRestsExport;
    }

    public void setDoRestsExport(boolean doRestsExport) {
        this.doRestsExport = doRestsExport;
    }

    private String restsExportCrontab = "";

    public String getRestsExportCrontab() {
        return restsExportCrontab;
    }

    public void setRestsExportCrontab(String restsExportCrontab) {
        this.restsExportCrontab = restsExportCrontab;
    }

    public List<SelectItem> getRestsExportCrontabs() {
        return (List<SelectItem>) RestsExportManager.selects();
    }


    //
    //  ----------------------------------------------------------------------------------------------------------------------------------------------------------
    //

    private List<ClientGroup> clientGroupsCache = gf().getClientGroups();
    private List<Brand> brandsCache = gf().getBrands();


    public Map<Long, Long> getSelectedClientgroups() {
        return getCurrent().getClientgroups();
    }

    private Map<Long, List<SelectItem>> clientgroups = null;

    public Map<Long, List<SelectItem>> getClientgroups() {
        if (clientgroups == null) {
            clientgroups = new HashMap<Long, List<SelectItem>>();

            for (Brand brand : brandsCache) {
                if (!clientgroups.containsKey(brand.getId()))
                    clientgroups.put(brand.getId(), new ArrayList<SelectItem>());
                clientgroups.get(brand.getId()).add(new SelectItem(0, "Не выбрана"));
            }

            for (ClientGroup _clientgroup : clientGroupsCache) {
                if (!clientgroups.containsKey(_clientgroup.getBrandId()))
                    clientgroups.put(_clientgroup.getBrandId(), new ArrayList<SelectItem>());
                clientgroups.get(_clientgroup.getBrandId()).add(new SelectItem(_clientgroup.getId(), _clientgroup.getName()));
            }
        }
        return clientgroups;
    }


    //  Видимость клиентских групп

    public String[] getSelectedVisibleClientgroups() {
        try {
            List<String> result = new ArrayList<String>();
            for (long l : getCurrent().getVisibleClientGroups()) result.add(String.valueOf(l));
            String[] r = new String[result.size()];
            for (int i = 0; i < r.length; i++)
                r[i] = result.get(i);
            return r;
        } catch (Exception e) {
            e.printStackTrace();
            return new String[0];
        }
    }

    public void setSelectedVisibleClientgroups(String[] selectedVisibleClientgroups) {
        try {
            getCurrent().getVisibleClientGroups().clear();
            for (String o : selectedVisibleClientgroups) {
                getCurrent().getVisibleClientGroups().add(new Long(o));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public List<SelectItem> getVisibleClientgroups() {
        //  EXPERIMENTAL
        try {
            List<SelectItem> result = new ArrayList<SelectItem>();
            for (Brand brand : brandsCache) {
                if (brand == null) continue;
                for (ClientGroup clientGroup : clientGroupsCache) {
                    if (clientGroup.getBrandId() == brand.getId())
                        result.add(new SelectItem(clientGroup.getId(), brand.getName() + "/" + clientGroup.getName()));
                }
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<SelectItem>();
        }
    }

    //  Видимость брэндов

    public String[] getSelectedVisibleBrands() {
        //  EXPERIMENTAL
        try {
            List<String> result = new ArrayList<String>();
            for (long l : getCurrent().getVisibleBrands()) result.add(String.valueOf(l));
            String[] r = new String[result.size()];
            for (int i = 0; i < r.length; i++)
                r[i] = result.get(i);
            return r;
        } catch (Exception e) {
            e.printStackTrace();
            return new String[0];
        }
    }

    public void setSelectedVisibleBrands(String[] selectedVisibleBrands) {
        try {
            getCurrent().getVisibleBrands().clear();
            for (String o : selectedVisibleBrands) {
                getCurrent().getVisibleBrands().add(new Long(o));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public List<SelectItem> getVisibleBrands() {
        List<SelectItem> result = new ArrayList<SelectItem>();
        for (Brand brand : brandsCache) {
            result.add(new SelectItem(brand.getId(), brand.getName()));
        }
        return result;
    }

    //  ----------------------------------------------------------------------------------------------------------------------------------------------------------

    /**
     * Отправить клиенту остатки
     */
    public void sendRests2ClientNow() {
        long clientId = getSessionBean().getEditableClientId();
        if (clientId <= 0) return;

        try {
            logger.debug("Sending rests to client: " + clientId);
            RestsExportManager.send(gf(), clientId);
            logger.debug("Rests were sent to client: " + clientId);
            facesInfo("Остатки были высланы клиенту");
        } catch (Exception e) {
            logger.error(e);
            e.printStackTrace();
            facesError("Не удалось отправить остатки клиенту");
        }
    }

    public List<SelectItem> getClientStateList(){
        List<SelectItem> list = new ArrayList<SelectItem>();
        list.add(new SelectItem(-1, "Не выбрано"));
        for (Pair pair : ClientState.list()) {
            list.add(new SelectItem(pair.getId(), pair.getValue()));
        }
        return list;
    }

    /**
     * Флаг, запрещающий редактирование параметров клиента
     */
    public boolean isReadonly() {
        return !getSecurityBean().getIsRoleAdministrator();
    }

    public void unlock(){
        Client client = getCurrent();
        client.setBlockCheck(false);
        try {
            gf().updateClient(client);
        } catch (ValidationException e) {
            logger.error(e.getMessage());
            facesError("Произошла ошибка. Пожалуйста, попробуйте позже или свяжитесь с Администратором");
        }
    }

}